package com.bee.bootstrap.plugs.privilege;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.bee.bootstrap.constant.RedisCacheKey;
import com.bee.bootstrap.plugs.redis.RedisCacheManager;
import com.bee.bootstrap.resource.entity.Resource;
import com.bee.bootstrap.resource.service.IResourceService;
import com.bee.bootstrap.roleResource.service.IRoleResourceService;
import com.bee.bootstrap.user.entity.User;
import com.bee.bootstrap.user.service.IUserService;
import com.bee.common.core.enums.StatusEnum;
import com.bee.common.core.jsonview.DataView;
import com.bee.common.tools.context.SpringContextUtil;
import com.bee.common.tools.regex.RestfulRegexUtils;
import com.bee.common.tools.web.PermissionConvert;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.List;

/**
 * 数据字段权限处理
 * @author liekkas
 *
 */
public class PrivilegeViewConverter extends
        MappingJackson2HttpMessageConverter {

    private final static Logger log = LoggerFactory.getLogger(PrivilegeViewConverter.class);

    private RedisCacheManager redisCacheManager;

    private IRoleResourceService roleResourceServiceImpl;

    private IUserService userServiceImpl;

    private IResourceService resourceServiceImpl;


    private static final ObjectMapper mapper = new ObjectMapper();

    public PrivilegeViewConverter()
    {
        super();
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        simpleModule.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<Enum> modifyEnumDeserializer(DeserializationConfig config,
                                                                 final JavaType type,
                                                                 BeanDescription beanDesc,
                                                                 final JsonDeserializer<?> deserializer) {
                return new JsonDeserializer<Enum>() {
                    @Override
                    public Enum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                        Enum e=null;
                        if(StringUtils.isNotBlank(jsonParser.getValueAsString())){
                            Class<? extends Enum> rawClass = (Class<Enum<?>>) type.getRawClass();
                            try {
                                e=Enum.valueOf(rawClass, jsonParser.getText().toUpperCase());
                            }catch (Exception ex){
                                return null;
                            }
                        }
                        return e;
                    }
                };
            }
        });
        objectMapper.registerModule(simpleModule);
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        setObjectMapper(objectMapper);
    }

    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return super.readInternal(clazz, inputMessage);
    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        if (object instanceof DataView && ((DataView) object).hasView())
        {
            HttpServletRequest request= SpringContextUtil.getRequest();
            String uri=((HttpServletRequest)request).getRequestURI();
            String method=((HttpServletRequest) request).getMethod().toLowerCase();
            String resourceCode=uri.split("/",-1)[1];
            String permission=resourceCode+":"+ PermissionConvert.permission(method);
            redisCacheManager= (RedisCacheManager) SpringContextUtil.getBean("redisCacheManager");
            List<Resource> resources= (List<Resource>) redisCacheManager.getCache(RedisCacheKey.RESOURCE_KEY).get(RedisCacheKey.RESOURCE_KEY);
            //缓存清除后重新加载
            if(resources==null||resources.size()==0){
                resourceServiceImpl= (IResourceService) SpringContextUtil.getBean("resourceServiceImpl");
                resources=resourceServiceImpl.selectList(new EntityWrapper<Resource>().eq("status", StatusEnum.NORMAL.toString()));
                redisCacheManager.getCache(RedisCacheKey.RESOURCE_KEY).put(RedisCacheKey.RESOURCE_KEY,resources);
            }
            Resource requestResource = null;
            if(resources!=null&&resources.size()>0){
                for(Resource resource:resources){
                    if(RestfulRegexUtils.obverseParametersRegex(uri).equals(RestfulRegexUtils.reverseParametersRegex(resource.getUrl()))&&
                            permission.equals(resource.getPermission())){
                        requestResource=resource;
                        break;
                    }
                }
                if(requestResource!=null){
                    roleResourceServiceImpl= (IRoleResourceService) SpringContextUtil.getBean("roleResourceService");
                    userServiceImpl=(IUserService) SpringContextUtil.getBean("userService");
                    User user=userServiceImpl.findUserByToken(StringUtils.defaultIfEmpty(request.getAttribute("token").toString(),""));
                    DataView dataView= roleResourceServiceImpl.findDataView(requestResource.getId(),user);
                    dataView.setData(((DataView) object).getData());
                    writeView(dataView, outputMessage);
                }
            }
        }else if(object instanceof String){
            try {
                log.debug("obj:  "+object);
                ObjectWriter writer = mapper.writer();
                writer.writeValueAsString(object);
            }
            catch (Exception ex) {
                throw new HttpMessageNotWritableException("Could not write text: " + ex.getMessage(), ex);
            }
        } else {
            super.writeInternal(object, type,outputMessage);
            log.debug("Json to client without View: {}",
                    super.getObjectMapper().writeValueAsString(object));
        }
    }

    protected void writeView(DataView view, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
        log.debug("Serializing using view {}", view.getView());
        JsonGenerator jsonGenerator =
                mapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        mapper.registerModule(simpleModule);
        try {
            mapper.writerWithView(view.getView()).writeValue(jsonGenerator, view.getData());
            log.debug("Json to client with View {}: {}", view.getView().getCanonicalName(),
                    mapper.writerWithView(view.getView()).writeValueAsString(view.getData()));
        }
        catch (IOException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
        }
    }

}
